package mts.strategies;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;

import mts.constants.Direction;
import mts.constants.StatusType;
import mts.groupdispatcher.FloorRequest;
import mts.messages.StatusMessage;

public class AdvancedStrategy extends MediaStrategy {

	private static final int WEIGHT_DIRECTION = 1000;
	private static final int WEIGHT_NUM_HALL_CALLS = 50;
	
	
	@Override
	public byte addFloorRequest(FloorRequest floorRequest) {
	
		HashMap<Byte, Integer> weightedElevatorControllers = new HashMap<Byte, Integer>();
		
		
		for (Byte carNum: groupDispatcher.getElevatorControllerStatuses().keySet()) {
			StatusMessage statusMessage = groupDispatcher.getElevatorControllerStatuses().get(carNum);
			int weight = 0;
		
			
			Collection<Byte> hallCalls = new ArrayList<Byte>();
			hallCalls.addAll(statusMessage.getCurrentHallCalls());
			hallCalls.addAll(groupDispatcher.getElevatorController(carNum).getPendingNewHallCalls());
			if (floorRequest.getFloor() > statusMessage.getCurrentFloor()) {
				// floor request is above current elevator controller
				
				if (statusMessage.getDirection() == Direction.UP || statusMessage.getStatus() == StatusType.SPOOLING) {
					// elevator is going in the right direction
					weight += WEIGHT_DIRECTION;
				} 
				
				for (Byte floor: hallCalls) {
					// if hall calls are above a DOWN floor request reduce this controllers overall weight
					if (floor > floorRequest.getFloor() && floorRequest.getDirection() == Direction.DOWN) {		
						// elevator is going above our floor
						weight -= WEIGHT_NUM_HALL_CALLS * 4;
					} else if (floor < floorRequest.getFloor() && floor > statusMessage.getCurrentFloor()) {
						// elevator has to stop before getting to our floor
						weight -= WEIGHT_NUM_HALL_CALLS;
					} else if (floor == floorRequest.getFloor()) {
						//elevator is already going to our floor
						weight += WEIGHT_NUM_HALL_CALLS * 2;
					}
					
					
				}
					
			} else if (floorRequest.getFloor() < statusMessage.getCurrentFloor()){
				// floor request is below current elevator controller
				
				if (statusMessage.getDirection() == Direction.DOWN || statusMessage.getStatus() == StatusType.SPOOLING) {
					weight += WEIGHT_DIRECTION;
				} 
				
				for (Byte floor: hallCalls) {
					// if hall calls are below an UP floor request reduce this controllers overall weight
					if (floor > floorRequest.getFloor() && floorRequest.getDirection() == Direction.UP) {		
						// elevator is going above our floor
						weight -= WEIGHT_NUM_HALL_CALLS * 4;
					} else if (floor > floorRequest.getFloor() && floor < statusMessage.getCurrentFloor()) {
						// elevator has to stop before getting to our floor
						weight -= WEIGHT_NUM_HALL_CALLS;
					} else if (floor == floorRequest.getFloor()) {
						//elevator is already going to our floor
						weight += WEIGHT_NUM_HALL_CALLS * 2;
					}
					
					
				}
				
			} else {
				// else its the same floor, we found the best elevator and return
				groupDispatcher.assignNewHallCallToElevatorController(carNum, floorRequest);
				return carNum;
			}
			
			weightedElevatorControllers.put(carNum, weight);
				
		}
		
		byte bestCarNum = -1;
		int highestValue = Integer.MIN_VALUE;
		for (Byte carNum: weightedElevatorControllers.keySet()) {
			Integer value = weightedElevatorControllers.get(carNum);
			
			if (value > highestValue) {
				bestCarNum = carNum;
				highestValue = value;
			}
		}
		
		groupDispatcher.assignNewHallCallToElevatorController(bestCarNum, floorRequest);
		return bestCarNum;
	}

}
